home *** CD-ROM | disk | FTP | other *** search
- Subject: v07i063: Nag reminder service, Part02/02
- Newsgroups: mod.sources
- Approved: mirror!rs
-
- Submitted by: rtech!daveb@rtech.uucp (Dave Brower)
- Mod.sources: Volume 7, Issue 63
- Archive-name: nag/Part02
-
- #!/bin/sh
- # This is a shell archive, meaning:
- # 1. Remove everything above the #!/bin/sh line.
- # 2. Save the resulting text in a file.
- # 3. Execute the file with /bin/sh (not csh) to create the files:
- # gdate.c
- # nag.c
- export PATH; PATH=/bin:$PATH
- echo shar: extracting "'gdate.c'" '(13521 characters)'
- if test -f 'gdate.c'
- then
- echo shar: over-writing existing file "'gdate.c'"
- fi
- sed 's/^X//' << \SHAR_EOF > 'gdate.c'
- X/*
- X * routines to turn a date from various formats to other formats
- X *
- X * Primary interesting routine is gdate() which eats a date
- X * string of several common formats (see comment) and
- X * fills in a standard UNIX tm structure.
- X *
- X * Barry Shein, Boston University
- X *
- X * if you compile it -DDEBUG (with DEBUG defined) it will
- X * pull in a main() routine to run standalone for testing.
- X *
- X * NOTE:
- X *
- X * Barry's gdate was broken by a 1-off error; tm_mon is kept
- X * in the range 0..11 instead of 1..12. Also, his totime was
- X * broken, so I've deleted it and use the tm_to_time() function
- X * from mod.sources.
- X *
- X *
- X * Defines the functions:
- X *
- X * lcase() -- convert a char to lower case
- X * dstring() -- get digit string from sp into bp (buffer) returning new sp
- X * skipw() -- skip white space returning updated ptr
- X * prefix() -- return how many chars of s1 prefix s2
- X * find() -- look up str in list for non-ambiguous (prefix) match
- X * lookup() -- like find but demands exact match
- X * extract() -- extract a token
- X * fill() -- fill up an area with a value (eg. zeros)
- X *
- X * gdate() -- convert a date/time string to a tm structure.
- X * gtime() -- convert time string to a tm structure.
- X *
- X * days() -- how many days were in a year.
- X * jan1() -- return day of the week of jan 1 of given year
- X * dowk() -- insert day of week given year and day of year into tm struct.
- X * doyr() -- insert partial day of year given yr, mon and mday into tm struct.
- X *
- X * leap() -- Return 1 if `y' is a leap year, 0 otherwise.
- X * ndays() -- number of days between UNIX epoch and time in a tm struct.
- X * tm_to_time() -- Convert a tm struct to a time_t.
- X */
- X
- X#include <stdio.h>
- X#include <ctype.h>
- X#include <sys/types.h>
- X
- X#ifdef SYS5
- X
- X# define time_t long /* SV is inconsistent, so go with lcd */
- X
- X# include <time.h>
- X# include <sys/times.h>
- X# include <string.h>
- X
- X# else /* BSD */
- X
- X# include <sys/time.h>
- X# include <sys/timeb.h>
- X# include <strings.h>
- X
- X#endif
- X
- X/*----------------------------------------------------------------
- X *
- X * Manifest constants
- X *
- X */
- X
- X#define MAXTOK 20
- X#define AMBIG -1 /* ambiguous month */
- X#define FALSE -2 /* bad syntax */
- X
- X/*----------------------------------------------------------------
- X *
- X * static and global Data
- X *
- X */
- X
- Xchar *months[] = {
- X "january", "february", "march", "april", "may", "june", "july",
- X "august", "september", "october", "november", "december", 0
- X} ;
- X
- Xchar *dow[] = {
- X "sunday", "monday", "tuesday", "wednesday", "thursday",
- X "friday", "saturday", 0
- X} ;
- X
- X /*
- X * known time-zone name list
- X */
- Xchar *tz[] =
- X{
- X "adt", "ast", "edt", "est", "cdt", "cst", "mst", "mdt",
- X "pdt", "pst", "gmt",
- X 0
- X} ;
- X
- Xchar mdays[] = {
- X 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
- X} ;
- X
- X/*----------------------------------------------------------------
- X *
- X * Utility functions
- X *
- X */
- X
- X/*
- X * lcase() -- convert a char to lower case
- X */
- Xlcase(c) register char c ;
- X{
- X return(isupper(c) ? tolower(c) : c) ;
- X}
- X
- X/*
- X * dstring() -- get digit string from sp into bp (buffer) returning new sp
- X */
- Xchar *
- Xdstring(bp,sp)
- X register char *sp, *bp ;
- X{
- X register int i = 0 ;
- X while(isdigit(*sp))
- X if(i++ == MAXTOK) break ;
- X else *bp++ = *sp++ ;
- X *bp = '\0' ;
- X return(sp) ;
- X}
- X
- X/*
- X * skipw() -- skip white space returning updated ptr
- X */
- Xchar *
- Xskipw(sp) register char *sp ;
- X{
- X while(isspace(*sp) || *sp == '-') ++sp ;
- X return(sp) ;
- X}
- X
- X/*
- X * prefix() -- return how many chars of s1 prefix s2
- X */
- Xprefix(s1,s2) register char *s1, *s2 ;
- X{
- X register int i = 0 ;
- X
- X for(; *s1 == *s2 ; s1++,s2++,i++)
- X if(*s2 == '\0') break ;
- X return(*s1 == '\0' ? i : 0) ;
- X}
- X
- X/*
- X * find() -- look up str in list for non-ambiguous (prefix) match
- X */
- Xfind(sp,lp) register char *sp, **lp ;
- X{
- X int i,j,ambig = 0 ;
- X register int k ;
- X int ret = FALSE ;
- X
- X for(i = 0,k = 0 ; *lp ; lp++,k++)
- X if((j = prefix(sp,*lp)) > i)
- X {
- X ambig = 0 ;
- X i = j ;
- X ret = k + 1 ;
- X }
- X else if(j && (i == j)) ++ambig ;
- X return(ambig ? AMBIG : ret) ;
- X}
- X
- X/*
- X * lookup() -- like find but demands exact match
- X */
- Xlookup(sp,lp) register char *sp, **lp ;
- X{
- X int i = 0 ;
- X
- X for(i=0 ; *lp ; lp++,i++)
- X if(strcmp(sp,*lp) == 0) return(i+1) ;
- X return(0) ;
- X}
- X
- X/*
- X * extract() -- extract a token
- X */
- Xchar *
- Xextract(bp,sp) register char *bp, *sp ;
- X{
- X register int i = 0 ;
- X
- X sp = skipw(sp) ;
- X for(; isalnum(*sp); sp++)
- X if(i++ == MAXTOK) break ;
- X else *bp++ = lcase(*sp) ;
- X *bp = '\0' ;
- X return(sp) ;
- X}
- X
- X/*
- X * fill() -- fill up an area with a value (eg. zeros)
- X */
- Xfill(cp,c,n) register char *cp, c ; register int n ;
- X{
- X while(n--) *cp++ = c ;
- X}
- X
- X/*----------------------------------------------------------------
- X *
- X * gdate, gtime related.
- X *
- X */
- X
- Xchar *gdate() ;
- Xchar *gtime() ;
- X
- X/*
- X * gdate(date_str_ptr,time_buf_ptr)
- X * (see CTIME(3) in UPM for second arg format)
- X * takes many reasonable date strings and translates to
- X * the time-buf structure (integers)
- X *
- X * formats (eg.):
- X * oct 19, 1983
- X * OcT 19, 1983 12:43
- X * oCt 19, 1983 12:43:33
- X * oct 19 1983 ...
- X * 19 oct 83 ....
- X * 10/19/83 12:43:33
- X * 19-OCT-83 12:43:00 (DEC style)
- X * Wed Oct 19 12:43 EST 83 (UNIX style)
- X * oct. 19, 83 1:44 pm est
- X * 831019124333 (see date(1))
- X * some variations on those themes also.
- X *
- X * BUGS: probably a few (maybe some in the eye of the beholder)
- X * does not set dst flag (unless 'EDT' is in string)
- X */
- Xchar *
- Xgdate(sp,tp) register char *sp ; register struct tm *tp ;
- X{
- X char buf[MAXTOK] ;
- X
- X fill(tp,'\0',sizeof *tp) ;
- X sp = skipw(sp) ;
- X if(isdigit(*sp)) /* either '7/12/83' or '12 jul 83' */
- X {
- X if(isdigit(sp[1]) && isdigit(sp[2])) /* UNIX yymmddhhmmss */
- X {
- X buf[2] = '\0' ;
- X (void)strncpy(buf,sp,2) ;
- X tp->tm_year = atoi(buf) ;
- X (void)strncpy(buf,sp += 2,2) ;
- X tp->tm_mon = atoi(buf) - 1 ;
- X sp += 2 ;
- X if(!isdigit(*sp)) goto badday ;
- X (void)strncpy(buf,sp,2) ;
- X tp->tm_mday = atoi(buf) ;
- X sp += 2 ;
- X if(!isdigit(*sp)) goto check ;
- X (void)strncpy(buf,sp,2) ;
- X
- X /* ??? formerly null effect "tp->tm_hour ;" */
- X
- X tp->tm_hour = atoi(buf) ;
- X sp += 2 ;
- X if(!isdigit(*sp)) goto check ;
- X (void)strncpy(buf,sp,2) ;
- X tp->tm_min = atoi(buf) ;
- X sp += 2 ;
- X if(!isdigit(*sp)) goto check ;
- X (void)strncpy(buf,sp,2) ;
- X tp->tm_min = atoi(buf) ;
- X goto check ;
- X }
- X sp = dstring(buf,sp) ;
- X sp = skipw(sp) ;
- X if(*sp == '/') /* must be '7/12/83' */
- X {
- X if((tp->tm_mon = atoi(buf) - 1) < 0 || (tp->tm_mon > 11))
- X {
- X tp->tm_mon = FALSE ;
- X goto badmon ;
- X }
- X sp = skipw(++sp) ;
- X if(!isdigit(*sp)) goto badday ;
- X sp = dstring(buf,sp) ;
- X tp->tm_mday = atoi(buf) ;
- X
- X sp = skipw(sp) ;
- X if(*sp != '/') goto badyr ;
- X sp = skipw(++sp) ;
- X if(!isdigit(*sp)) goto badyr ;
- X sp = dstring(buf,sp) ;
- X tp->tm_year = atoi(buf) ;
- X
- X sp = gtime(sp,tp) ;
- X }
- X else
- X {
- X /*
- X * must be '12 jul 83'
- X */
- X tp->tm_mday = atoi(buf) ;
- X
- X sp = extract(buf,sp) ;
- X if((tp->tm_mon = find(buf,months)) < 0) goto badmon ;
- X
- X if(*sp == '.') ++sp ;
- X sp = skipw(sp) ;
- X
- X if(!isdigit(*sp)) goto badyr ;
- X sp = dstring(buf,sp) ;
- X tp->tm_year = atoi(buf) ;
- X
- X sp = gtime(sp,tp) ;
- X }
- X }
- X else
- X {
- X int flag = 0 ; /* used to indicate looking for UNIX style */
- X
- X /*
- X * either 'jul 12 83' or (UNIX) Wed jul 12 18:33 EST 1983
- X */
- X sp = extract(buf,sp) ;
- X if(find(buf,dow) > 0)
- X {
- X sp = extract(buf,sp) ;
- X ++flag ;
- X }
- X
- X if((tp->tm_mon = find(buf,months)) < 0) goto badmon ;
- X
- X if(*sp == '.') ++sp ;
- X sp = skipw(sp) ;
- X
- X if(!isdigit(*sp)) goto badday ;
- X sp = dstring(buf,sp) ;
- X tp->tm_mday = atoi(buf) ;
- X
- X sp = skipw(sp) ;
- X if(*sp == ',') sp++ ;
- X sp = skipw(sp) ;
- X
- X if(flag) sp = skipw(gtime(sp,tp)) ;
- X
- X if(!isdigit(*sp)) goto badyr ;
- X sp = dstring(buf,sp) ;
- X tp->tm_year = atoi(buf) ;
- X
- X if(!flag) sp = gtime(sp,tp) ;
- X }
- X check:
- X /*
- X * check for ridiculous numbers
- X */
- X if(tp->tm_mday < 1) goto badday ;
- X if(tp->tm_mday > mdays[tp->tm_mon])
- X if(!((tp->tm_mon == 1) && /* check for Feb 29 */
- X (tp->tm_mday == 29) && (days(tp->tm_year) == 365) ))
- X goto badday ;
- X if(tp->tm_year >= 1900) tp->tm_year -= 1900 ;
- X if(tp->tm_hour > 23)
- X {
- X tp->tm_hour = FALSE ;
- X return(sp) ;
- X }
- X if(tp->tm_min > 59)
- X {
- X tp->tm_hour = FALSE ;
- X return(sp) ;
- X }
- X if(tp->tm_sec > 59)
- X {
- X tp->tm_sec = FALSE ;
- X return(sp) ;
- X }
- X /*
- X * fill in day of year, day of week (these calls must be
- X * in this order as dowk() needs doyr()
- X */
- X
- X doyr(tp) ;
- X dowk(tp) ;
- X /*
- X * all done !
- X */
- X return(NULL) ;
- X badday :
- X tp->tm_mday = FALSE ;
- X return(sp) ;
- X badmon :
- X return(sp) ;
- X badyr :
- X tp->tm_year = FALSE ;
- X return(sp) ;
- X}
- X
- X/*
- X * gtime() -- get hh:mm:ss or equivalent into a tm struct.
- X */
- Xchar *
- Xgtime(sp,tp) register char *sp ; register struct tm *tp ;
- X{
- X char buf[MAXTOK],*cp ;
- X
- X sp = skipw(sp) ;
- X if(isdigit(*sp))
- X {
- X sp = dstring(buf,sp) ;
- X tp->tm_hour = atoi(buf) ;
- X sp = skipw(sp) ;
- X if(*sp == ':') sp = skipw(++sp) ;
- X else goto out ;
- X if(isdigit(*sp))
- X {
- X sp = dstring(buf,sp) ;
- X tp->tm_min = atoi(buf) ;
- X sp = skipw(sp) ;
- X if(*sp == ':') sp = skipw(++sp) ;
- X else goto out ;
- X if(isdigit(*sp))
- X {
- X sp = dstring(buf,sp) ;
- X tp->tm_sec = atoi(buf) ;
- X }
- X }
- X }
- X out :
- X sp = skipw(sp) ;
- X if(isalpha(*sp)) /* PM:AM or time zone or both */
- X {
- X cp = extract(buf,sp) ;
- X if(strcmp(buf,"am") == 0 || strcmp(buf,"pm") == 0)
- X {
- X if(buf[0] == 'p' && tp->tm_hour < 12)
- X tp->tm_hour += 12 ;
- X sp = cp = skipw(cp) ;
- X cp = extract(buf,cp) ;
- X }
- X if(lookup(buf,tz))
- X {
- X if(buf[1] == 'd') tp->tm_isdst++ ;
- X sp = skipw(cp) ;
- X }
- X }
- X return (sp);
- X}
- X
- X/*
- X * days() -- how many days were in a year.
- X *
- X * Ok, you were all wondering so here it is folks...
- X * THE LEAP YEAR CALCULATION
- X * note: does not take into account 1752.
- X */
- Xdays(y) register int y ;
- X{
- X if(y < 1970) y += 1900 ;
- X if(((y % 4) == 0) && ( (y % 100) || ((y % 400)==0) )) y = 366 ;
- X else y = 365 ;
- X return(y) ;
- X}
- X
- X
- X/*
- X * jan1() -- return day of the week of jan 1 of given year
- X */
- Xjan1(yr)
- X{
- X register y, d;
- X
- X /*
- X * normal gregorian calendar
- X * one extra day per four years
- X */
- X
- X y = yr;
- X d = 4+y+(y+3)/4;
- X
- X /*
- X * julian calendar
- X * regular gregorian
- X * less three days per 400
- X */
- X
- X if(y > 1800) {
- X d -= (y-1701)/100;
- X d += (y-1601)/400;
- X }
- X
- X /*
- X * take care of weirdness at 1752.
- X */
- X
- X if(y > 1752)
- X d += 3;
- X
- X return(d%7);
- X}
- X
- X/*
- X * dowk() -- insert day of week given year and day of year into tm struct.
- X */
- Xdowk(tp) register struct tm *tp ;
- X{
- X tp->tm_wday = (jan1(tp->tm_year+1900) + tp->tm_yday) % 7 ;
- X}
- X
- X/*
- X * doyr() -- insert partial day of year given yr and mon into tm struct.
- X */
- Xdoyr(tp) register struct tm *tp ;
- X{
- X register int i,j ;
- X
- X j = ((tp->tm_mon > 1) && (days(tp->tm_year) == 366)) ? 1 : 0 ;
- X for(i=1 ; i < tp->tm_mon ; i++)
- X j += mdays[i-1] ;
- X tp->tm_yday = j + tp->tm_mday - 1 ;
- X}
- X
- X/*----------------------------------------------------------------
- X *
- X * tm_to_time related
- X *
- X */
- X
- X/*
- X * leap() -- Return 1 if `y' is a leap year, 0 otherwise.
- X */
- Xstatic int
- Xleap (y)
- X int y;
- X{
- X y += 1900;
- X if (y % 400 == 0)
- X return (1);
- X if (y % 100 == 0)
- X return (0);
- X return (y % 4 == 0);
- X}
- X
- X/*
- X * ndays() -- number of days since UNIX epoch and time in a tm struct.
- X */
- Xstatic int
- Xndays (p)
- X struct tm *p;
- X{
- X register n = p->tm_mday;
- X register m, y;
- X register char *md = "\37\34\37\36\37\36\37\37\36\37\36\37";
- X
- X for (y = 70; y < p->tm_year; ++y) {
- X n += 365;
- X if (leap (y)) ++n;
- X }
- X for (m = 0; m < p->tm_mon; ++m)
- X n += md[m] + (m == 1 && leap (y));
- X return (n);
- X}
- X
- X/*
- X * tm_to_time() -- Convert a tm struct to a time_t.
- X *
- X * returns 0 if the time is before the UNIX epoch, 1/1/70 00:00:00
- X */
- Xtime_t
- Xtm_to_time (tp)
- X struct tm *tp;
- X{
- X register int m1, m2;
- X time_t t;
- X struct tm otm;
- X
- X /* special case date before epoch */
- X if( tp->tm_year < 70) return(0);
- X
- X t = (ndays (tp) - 1) * 86400L + tp->tm_hour * 3600L
- X + tp->tm_min * 60 + tp->tm_sec;
- X /*
- X * Now the hard part -- correct for the time zone:
- X */
- X otm = *tp;
- X tp = localtime (&t);
- X m1 = tp->tm_hour * 60 + tp->tm_min;
- X m2 = otm.tm_hour * 60 + otm.tm_min;
- X t -= ((m1 - m2 + 720 + 1440) % 1440 - 720) * 60L;
- X return (t);
- X}
- X
- X/*----------------------------------------------------------------
- X *
- X * Test program related
- X *
- X */
- X
- X#if DEBUG
- X/*
- X * test driver
- X * translates first arg from command line (argv[1])
- X * and dumps out structure built.
- X */
- Xusage(sp) char *sp ;
- X{
- X fprintf(stderr,"Usage: %s date\n",sp) ;
- X exit(1) ;
- X}
- X
- X/*
- X * main() -- test the gdate and tm_to_time routines
- X */
- Xmain(argc,argv) int argc ; char **argv ;
- X{
- X char *cp ;
- X struct tm tm ;
- X time_t t,t2 ;
- X char *asctime();
- X char *ctime();
- X
- X if(argc != 2) usage(*argv) ;
- X
- X if((cp = gdate(*++argv,&tm)) != NULL)
- X printf("error: %s (%s)\n",*argv,cp) ;
- X
- X printf("year : %d month: %d day: %d\n",
- X tm.tm_year,tm.tm_mon,tm.tm_mday);
- X printf("day of month: %d hour: %d minute: %d second: %d\n",
- X tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec) ;
- X printf("day of year: %d day of week: %d dst: %d\n",
- X tm.tm_yday, tm.tm_wday, tm.tm_isdst) ;
- X
- X t = time(NULL) ;
- X t2 = tm_to_time(&tm) ;
- X
- X printf("time_t of now %d, of arg %d\n",t, t2 ) ;
- X printf("Now: %s", ctime(&t) );
- X printf("Arg: %s", ctime(&t2) );
- X exit(0) ;
- X}
- X
- X#endif /* DEBUG */
- X
- SHAR_EOF
- if test 13521 -ne "`wc -c 'gdate.c'`"
- then
- echo shar: error transmitting "'gdate.c'" '(should have been 13521 characters)'
- fi
- echo shar: extracting "'nag.c'" '(29846 characters)'
- if test -f 'nag.c'
- then
- echo shar: over-writing existing file "'nag.c'"
- fi
- sed 's/^X//' << \SHAR_EOF > 'nag.c'
- X/*----------------------------------------------------------------
- X *
- X * Nag.c -- annoying reminder service daemon
- X *
- X * Sun Aug 24 14:18:08 PDT 1986
- X *
- X * by Dave Brower, {amdahl, cbosgd, mtxinu, sun}!rtech!gonzo!daveb
- X *
- X * Copyright 1986, David C Brower. All rights reserved.
- X *
- X * This is a preliminary version. The final release will be offered
- X * with fewer restrictions.
- X *
- X * Nag should be launched out of your .login or .profile. It
- X * periodically reads your ~/.nag file and executes commands
- X * that can be used as reminders of upcoming events. The environment
- X * variable NAGFILE can be used to get input from something other than
- X * the ~/.nag file.
- X *
- X * NAGFILE FORMAT:
- X * ---------------
- X *
- X * The ~/.nag file should contain lines of the form:
- X *
- X * status day time interval command
- X *
- X * where:
- X *
- X * status is one of
- X * 1. '#' indicating a commented out reminder
- X * 2. ':' indicating a silenced reminder
- X * 3. ' ' for an activate reminder.
- X * Other values produce unpredicatable results.
- X *
- X * day is one of:
- X * 1. A date, "8/8/88", "8-Aug-88", etc., but no blanks.
- X * 2. '*' for any day.
- X * 3. A day, "Sun", "Mon", ...
- X * The last is presently unimplemented (sorry).
- X *
- X * time is a time spec, "8AM", "23:00", etc., but no blanks
- X *
- X * interval is a colon separated list of minutes after time at which
- X * to execute the command, e.g.,
- X *
- X * -30:-15:0:5:10
- X *
- X * produces execution 30 and 15 minutes before the event,
- X * at the time, and 5 and 10 minutes later.
- X *
- X * command is a command to execute with /bin/sh. Some shell variables
- X * are set for use in messages:
- X *
- X * $pretime -interval
- X * $posttime interval
- X * $now hh:mm of the current time
- X * $then hh:mm of the parent event
- X *
- X * Blank lines are ignored.
- X *
- X * Example:
- X *
- X * # don't forget to eat.
- X * * 12:30PM 0 writebig "Lunch Time"
- X *
- X * # Weekly warning that has been silenced.
- X * :Mon 3:00PM -30:-20:-10:-5:0 echo "^GStatus report in $time"
- X *
- X * # Active Weekly warning.
- X * Fri 1:30PM -20:-10:-5:0:5:10 echo "^GCommittee meeting in $time"
- X *
- X * # One shot warning to call the east coast.
- X * 8/25/86 1:30PM -180:-120:-60:0:10:20 echo "^GCall DEC Marlblerow"
- X *
- X * NAG
- X * ---
- X *
- X * Nag puts itself in the background, and exits when you logout.
- X * Standard output and standard error go to your terminal.
- X *
- X * Each time it wakes up, it sees if the ~/.nag file has changed.
- X * If so, it builds an event queue for lines without '#' comment symbols.
- X *
- X * Events that were not silenced with 'X' and were due before "now"
- X * are executed. If the event was the last for an entry in ~/.nag,
- X * the file is edited to re-enable it from a gagged state.
- X *
- X * The program then sleeps for at most MINSLEEP minutes.
- X *
- X * OKOK
- X * ----
- X *
- X * The "okok" program just edits ~/.nag and prepends an 'X' to lines
- X * that need to be shut up.
- X *
- X * BUILD INSTRUCTIONS:
- X * -------------------
- X *
- X * cc -o nag [ -DSYS5 ] nag.c gdate.c
- X * ln nag okok
- X *
- X * The code compiles for a BSD system by default.
- X *
- X * CAVEATS:
- X * --------
- X *
- X * Sorry Christopher, it probably won't work if stty nostop is set.
- X *
- X */
- X
- X# include <stdio.h>
- X# include <sys/types.h>
- X# include <sys/stat.h>
- X# include <signal.h>
- X# include <pwd.h>
- X# include <ctype.h>
- X
- X# ifdef SYS5
- X# include <string.h>
- X# include <time.h>
- X# define index strchr
- X# define rindex strrchr
- X# else
- X# include <strings.h>
- X# include <sys/time.h>
- X# endif
- X
- X/*----------------
- X *
- X * defines
- X *
- X */
- X
- X# define DPRINTF if(Debug) (void)fprintf
- X
- X# define COMCHAR '#'
- X# define SILCHAR ':'
- X
- X# define HRSECS 3600L
- X
- X# define CTIMELEN 32 /* length of a date/time string */
- X
- X# define MINSLEEP (5*60)
- X# define MAXARGS 5120 /* max arg/env size on System V */
- X
- X# define TRUE (1)
- X# define FALSE (0)
- X
- X# define min(a,b) ((a) < (b) ? (a) : (b))
- X
- X/*----------------
- X *
- X * typedefs and structure definitions
- X *
- X */
- X
- X/*
- X * A NAGLINE is a parsed entry from the .nag file. We keep
- X * a list of them representing the current file, so we can
- X * write it back out easily.
- X */
- X
- Xtypedef struct nagline NAGLINE;
- X
- Xstruct nagline
- X{
- X NAGLINE * next; /* Next in the chain */
- X int type; /* COMMENT, SILENT, PENDING, BAD */
- X# define UNKNOWN 0
- X# define COMMENT 1
- X# define SILENT 2
- X# define PENDING 3
- X# define BAD 4
- X
- X int errtype; /* if type is BAD, cause of error */
- X# define NOERR 0
- X# define EMPTY 1
- X# define DATEBAD 2
- X# define NOTIME 3
- X# define TIMEBAD 4
- X# define NOINTERVALS 5
- X# define NOCMD 6
- X
- X time_t atime; /* absolute time of event */
- X char *err; /* string that caused the error */
- X char *line; /* the raw line, allocated */
- X char *datestr; /* the date string, allocated */
- X char *timestr; /* the time string, allocated */
- X char *intstr; /* extracted string of intervals, allocated */
- X char *cmd; /* extracted command to execute, allocated */
- X};
- X
- Xstatic
- Xchar *linetypes[] =
- X{
- X "Unknown",
- X "Comment",
- X "Silent",
- X "Pending",
- X "Bad"
- X};
- X
- Xstatic
- Xchar *parserrs[] =
- X{
- X "No error",
- X "Empty line",
- X "Bad date",
- X "No time",
- X "Bad time",
- X "No intervals",
- X "No command"
- X};
- X
- X
- X/*
- X * An EVENT is an entry in the event queue.
- X */
- X
- Xtypedef struct event EVENT;
- X
- Xstruct event
- X{
- X EVENT * next; /* next event in chain */
- X NAGLINE *lp; /* the parent nagline */
- X time_t etime; /* absolute time of the event */
- X int offset; /* minutes difference with parent time */
- X};
- X
- X
- X/*----------------
- X *
- X * File local variables
- X *
- X */
- X
- Xstatic char *Myname=""; /* name from argv[0] */
- Xstatic time_t Now = 0; /* absolute time of "now" */
- Xstatic time_t Last = 0; /* time last time we were awake */
- Xstatic NAGLINE *Flist = NULL; /* lines from the file */
- Xstatic NAGLINE *Flast = NULL; /* last line from the file */
- Xstatic EVENT *Evq = NULL; /* the global event queue */
- Xstatic char Origlogin[20] = ""; /* login name when program started */
- Xstatic char Nagfile[ 256 ] = ""; /* full path of the nag file */
- Xstatic int Debug = FALSE; /* debugging? */
- X
- Xstatic char Laststr[ CTIMELEN ]; /* ctime output for last time through */
- Xstatic char Nowstr[ CTIMELEN ]; /* ctime output for this time through */
- X
- X/*----------------
- X *
- X * Forward and external function definitions
- X *
- X */
- X
- X/* library defined */
- X
- Xextern char *getlogin(); /* login name in /etc/utmp */
- Xextern char *getenv(); /* get an environment variable */
- Xextern struct passwd *getpwuid(); /* passwd entry for this user */
- Xextern time_t time();
- Xextern struct tm *localtime();
- Xextern char *fgets();
- Xextern char *index();
- Xextern char *rindex();
- Xextern int sprintf();
- Xextern void perror(); /* int on BSD? */
- Xextern void qsort(); /* int on BSD? */
- Xextern unsigned sleep();
- Xextern void free();
- Xextern void exit();
- Xextern char *ctime();
- X
- X/* gdate.c defined */
- X
- Xextern char *gdate(); /* date string to time buf struct */
- Xextern char *gtime(); /* time string to time buf struct */
- Xextern time_t tm_to_time(); /* time buf to secs past epoch */
- Xextern char *dow[]; /* days of the week names */
- Xextern int find(); /* unambiguous search of string tables */
- X
- X/* forward function references */
- X
- X# define forward extern
- X
- Xforward void nagfile();
- Xforward void setup();
- X
- Xforward int readf();
- Xforward int editf();
- Xforward int writef();
- X
- Xforward int parseline();
- Xforward void zaplines();
- X
- Xforward void buildq();
- Xforward void zapq();
- Xforward void insq();
- Xforward void addevents();
- Xforward int timecmp();
- Xforward void sortq();
- Xforward void runq();
- X
- Xforward void showlines();
- Xforward void dumpline();
- X
- Xforward void showevents();
- Xforward void dumpevent();
- X
- Xforward char *emalloc();
- Xforward char *ecalloc();
- Xforward FILE *efopen();
- X
- Xforward char *nctime();
- Xforward char *nhour();
- Xforward void delay();
- Xforward void lowcase();
- X
- X/*----------------
- X *
- X * main() -- Main program.
- X *
- X * Do one time setup, then go into a loop rebuilding the event queue,
- X * executing events in order. Sleep is done after running the queue.
- X *
- X */
- X/*ARGSUSED*/
- Xmain(argc, argv)
- Xint argc;
- Xchar **argv;
- X{
- X char *cp;
- X
- X if(argc > 1)
- X Debug = TRUE;
- X
- X Myname = (cp = rindex(argv[0], '/')) ? cp + 1 : argv[0] ;
- X nagfile();
- X
- X if( !strcmp(Myname, "nag") )
- X {
- X setup();
- X
- X# ifndef FOREGROUND
- X DPRINTF(stderr, "forking to self-backgrounnd");
- X if(fork())
- X exit(0);
- X# endif
- X /* pretend we started at the epoch */
- X Now = 0;
- X (void) strcpy( Nowstr, nctime( &Now ));
- X
- X /*
- X * This loop never exits.
- X *
- X * The program terminates in delay() when the user logs
- X * off this terminal.
- X */
- X for(;;)
- X {
- X (void) strcpy( Laststr, Nowstr );
- X Last = Now;
- X
- X Now = time(NULL);
- X (void) strcpy( Nowstr, nctime( &Now ) );
- X
- X DPRINTF(stderr, "\nLoop:\tLast %s\tNow %s\n", Laststr, Nowstr);
- X
- X if ( readf() )
- X buildq();
- X
- X runq();
- X }
- X }
- X else if ( !strcmp(Myname, "okok"))
- X {
- X Now = time( NULL );
- X (void) strcpy( Nowstr, nctime( &Now ));
- X
- X if ( readf() )
- X {
- X buildq();
- X if ( editf( PENDING ) )
- X exit( writef() );
- X }
- X else
- X {
- X (void) fprintf(stderr, "%s: Can't read %s\n", Myname, Nagfile );
- X exit(1);
- X }
- X }
- X else
- X {
- X (void) fprintf(stderr, "Identity crisis: \"%s\" bad program name\n",
- X argv[0]);
- X exit(1);
- X }
- X exit(0);
- X /*NOTREACHED*/
- X}
- X
- X/*----------------
- X *
- X * nagfile -- get the full .nag file path
- X *
- X */
- Xvoid
- Xnagfile()
- X{
- X register char *home;
- X register char *cp;
- X
- X /* remember who you are to check for logout later */
- X
- X (void) strcpy(Origlogin, getlogin());
- X
- X /* expand the Nagfile name */
- X
- X if( cp = getenv("NAGFILE") )
- X (void)strcpy( Nagfile, cp );
- X else if( home = getenv("HOME") )
- X (void) sprintf( Nagfile, "%s/.nag", home );
- X else
- X {
- X (void) fprintf(stderr, "%s: HOME is not set\n", Myname );
- X exit(1);
- X }
- X
- X DPRINTF(stderr, "Origlogin %s, Nagfile %s\n", Origlogin, Nagfile);
- X}
- X
- X/*----------------
- X *
- X * setup() -- one time initialization.
- X *
- X * Setup signals so we don't go away.
- X * accidentally.
- X *
- X */
- Xvoid
- Xsetup()
- X{
- X if(!Debug)
- X {
- X (void) signal( SIGQUIT, SIG_IGN );
- X (void) signal( SIGTERM, SIG_IGN );
- X# ifdef SIGTTOU
- X (void) signal( SIGTTOU, SIG_IGN );
- X# endif
- X }
- X}
- X
- X
- X/*----------------
- X *
- X * readf() -- read the nagfile and build in memory copy.
- X *
- X * Returns TRUE if the file was read.
- X */
- Xint
- Xreadf()
- X{
- X register NAGLINE *lp;
- X register FILE *fp;
- X char line[ MAXARGS ];
- X struct stat newstat;
- X static struct stat laststat = { 0 };
- X static time_t readtime = 0;
- X
- X /* check to see if Nagfile has changed, and reread file. */
- X
- X if(stat(Nagfile, &newstat))
- X {
- X /* set it the epoch, but don't complain */
- X newstat.st_mtime = 0;
- X }
- X
- X /* if file changed, or we read it more than 12 hours ago */
- X
- X if ( newstat.st_mtime <= laststat.st_mtime
- X || (readtime && Now > 0 && readtime < (Now - (HRSECS * 12))))
- X {
- X DPRINTF(stderr, "already read %s\n", Nagfile );
- X return FALSE;
- X }
- X
- X /* rebuild the internal copy of the file */
- X
- X DPRINTF(stderr, "reading Nagfile\n");
- X
- X laststat = newstat;
- X readtime = Now;
- X
- X zaplines();
- X
- X /* warn, but don't fatal if file can't be opened this time through */
- X
- X if ( NULL==(fp = efopen(Nagfile, "r")))
- X return FALSE;
- X
- X /* build the new incore copy */
- X
- X while( NULL != fgets( line, sizeof(line), fp ) )
- X {
- X /* Lose trailing newline */
- X line[ strlen(line) - 1 ] = '\0';
- X
- X /*ALIGNOK*/
- X lp = (NAGLINE *) ecalloc( sizeof(*lp), 1 );
- X
- X if( parseline( line, lp ) )
- X {
- X if( lp->type == BAD )
- X DPRINTF(stderr, "Parsed OK: %s\n", lp->line );
- X else
- X DPRINTF(stderr, "Parsed OK: %s %s %s %s\n",
- X lp->datestr,
- X lp->timestr,
- X lp->intstr,
- X lp->cmd );
- X }
- X else
- X {
- X (void) fprintf(stderr, "%s: Can't parse line:\n%s\n%s %s\n",
- X Myname,
- X lp->line,
- X parserrs[ lp->errtype ],
- X lp->err );
- X }
- X
- X if( !Flist )
- X Flist = lp;
- X if( Flast )
- X Flast->next = lp;
- X Flast = lp;
- X }
- X (void) fclose(fp);
- X
- X if(Debug)
- X {
- X (void) fprintf(stderr, "Read file OK\n");
- X showlines( "\nLines after file read in:\n" );
- X }
- X
- X return TRUE;
- X}
- X
- X
- X/*----------------
- X *
- X * editf() -- interactively edit the nag file in memory, then write it out.
- X *
- X * Used by 'okok' to make PENDING events SILENT; can also be used to
- X * make SILENT events PENDING.
- X *
- X * Goes WAY out of it's way to force i/o to be on the terminal.
- X *
- X * Returns TRUE if lines were changed.
- X */
- Xint
- Xeditf( what )
- Xregister int what;
- X{
- X register FILE *ifp;
- X register FILE *ofp;
- X register NAGLINE *lp;
- X register EVENT *ep;
- X register int changed = FALSE;
- X
- X char buf[ 80 ];
- X
- X if( ( ifp = efopen( "/dev/tty", "r" ) ) == NULL )
- X return( changed );
- X
- X if( ( ofp = efopen( "/dev/tty", "w" ) ) == NULL )
- X return( changed );
- X
- X setbuf( ofp, NULL ); /* force output to be unbuffered */
- X
- X for( lp = Flist; lp ; lp = lp->next )
- X {
- X if( lp->type == what )
- X {
- X /* only display events on the queue within 12 hours */
- X
- X for( ep = Evq; ep && ep->lp != lp; ep = ep->next )
- X continue;
- X
- X if( !ep || ep->etime > Now + (HRSECS * 12) )
- X continue;
- X
- X (void) fprintf( ofp, "Silence %s: %s (y/n/q)? ",
- X lp->timestr, lp->cmd ) ;
- X
- X if( fgets( buf, sizeof(buf), ifp ) == NULL )
- X break;
- X
- X if( buf[ 0 ] == 'y' || buf[ 0 ] == 'Y' )
- X {
- X lp->type = ( what == PENDING ) ? SILENT : PENDING;
- X changed = TRUE;
- X }
- X
- X /* stop querying if a 'q' is entered */
- X
- X if( buf[ 0 ] == 'q' || buf[ 0 ] == 'Q' )
- X break;
- X }
- X }
- X (void) fclose( ifp );
- X (void) fclose( ofp );
- X return ( changed );
- X}
- X
- X
- X/*----------------
- X *
- X * writef() -- Write the file back out after a change.
- X *
- X * Returns TRUE if file wrote OK.
- X */
- Xint
- Xwritef()
- X{
- X char buf[ 80 ];
- X
- X register int err;
- X register FILE *fp;
- X register NAGLINE *lp;
- X
- X DPRINTF(stderr, "Writing %s\n", Nagfile );
- X
- X if( ( fp = efopen( Nagfile, "w" ) ) == NULL )
- X return (FALSE);
- X
- X err = 0;
- X for( lp = Flist; lp && err >= 0 ; lp = lp->next )
- X {
- X switch( lp->type )
- X {
- X case BAD:
- X case COMMENT:
- X err = fprintf( fp, "%s\n", lp->line );
- X break;
- X default:
- X err = fprintf( fp, "%c%s %s %s %s\n",
- X lp->type == SILENT ? SILCHAR : ' ',
- X lp->datestr,
- X lp->timestr,
- X lp->intstr,
- X lp->cmd );
- X break;
- X }
- X }
- X
- X if( err < 0 )
- X {
- X DPRINTF( stderr, "err %d\n", err );
- X (void) sprintf( buf, "%s: error writing %s", Myname, Nagfile );
- X perror( buf );
- X }
- X else if( (err = fclose( fp ) ) < 0 )
- X {
- X (void) sprintf( buf, "%s: error closing %s", Myname, Nagfile );
- X perror( buf );
- X return( FALSE );
- X }
- X return ( err >= 0 );
- X}
- X
- X
- X/*----------------
- X *
- X * parseline() -- Split text into a NAGLINE more amenable to processing.
- X *
- X * Returns TRUE with the NAGLINE all set up if parsed OK.
- X * Returns FALSE with the line->type set to BAD,
- X * and line->errtype set if undecipherable.
- X *
- X *
- X * in the code, buf points to the first character not processed,
- X * cp points to the last character examined.
- X *
- X * cp places nulls in likely places.
- X *
- X * This is a very ugly function and should be rewritten.
- X */
- Xint
- Xparseline( buf, lp )
- Xregister char *buf;
- Xregister NAGLINE *lp;
- X{
- X register char *cp;
- X register int today;
- X register int i;
- X time_t d;
- X time_t t;
- X int anyday;
- X struct tm ntm; /* now tm struct */
- X struct tm dtm; /* date tm struct */
- X struct tm ttm; /* time tm struct */
- X
- X anyday = FALSE;
- X lp->line = strcpy( emalloc( strlen( buf ) + 1 ), buf );
- X
- X /*
- X * determine line type, and advance buf to first non-blank after
- X * the status field
- X */
- X
- X switch (*buf)
- X {
- X case COMCHAR:
- X lp->type = COMMENT;
- X return TRUE;
- X /*NOTREACHED*/
- X
- X case SILCHAR:
- X lp->type = SILENT;
- X buf++;
- X break;
- X
- X default:
- X lp->type = PENDING;
- X break;
- X }
- X
- X /* skip to non-whitespace */
- X
- X while( *buf && isspace(*buf))
- X buf++;
- X
- X /* empty line isn't fatal (it's a comment) */
- X
- X if (!*buf) {
- X lp->type = BAD;
- X lp->errtype = EMPTY;
- X lp->err = buf;
- X return TRUE;
- X }
- X
- X /* bracket the day/date, and null terminate it */
- X
- X for( cp = buf; *cp && !isspace( *cp ); cp++ )
- X continue;
- X if( *cp ) *cp++ = '\0';
- X else *cp = '\0';
- X
- X /* cp now positioned at char past null, or on null at the end */
- X
- X /*
- X * buf points at the day field; figure out the
- X * absolute time of "Midnight" of the right day for the event.
- X */
- X lp->datestr = strcpy( emalloc( strlen( buf ) + 1 ), buf );
- X
- X /* figure when midnight of today was */
- X
- X ntm = *localtime( &Now );
- X ntm.tm_sec = 0;
- X ntm.tm_min = 0;
- X ntm.tm_hour = 0;
- X
- X if (*buf == '*')
- X {
- X anyday = TRUE;
- X dtm = ntm;
- X }
- X else
- X {
- X
- X /* parse date */
- X
- X if( NULL != gdate( buf, &dtm ) )
- X {
- X DPRINTF(stderr, "not a date, maybe a day\n");
- X
- X /* maybe it's a day name... */
- X
- X lowcase( buf );
- X if( (i = find( buf, dow )) >= 0 )
- X {
- X i--;
- X today = ntm.tm_wday;
- X DPRINTF(stderr, "today %s, event %s\n",
- X dow[ today ],
- X dow[ i ] );
- X if( i < today )
- X i += 7; /* it's next week */
- X d = Now + (( i - today ) * HRSECS * 24 );
- X dtm = *localtime( &d );
- X dtm.tm_sec = 0;
- X dtm.tm_min = 0;
- X dtm.tm_hour = 0;
- X }
- X else
- X {
- X DPRINTF(stderr, "find of %s in dow returned %d\n", buf, i );
- X lp->type = BAD;
- X lp->errtype = DATEBAD;
- X lp->err = buf;
- X return FALSE;
- X }
- X }
- X }
- X
- X d = tm_to_time( &dtm );
- X DPRINTF(stderr, "parseline: date %s\n", nctime(&d) );
- X
- X /* advance to time */
- X
- X for( buf = cp ; *buf && isspace(*buf); buf++) /* skip blanks */
- X continue;
- X
- X if (!*buf) {
- X lp->type = BAD;
- X lp->errtype = NOTIME;
- X lp->err = buf;
- X return FALSE;
- X }
- X
- X /* bracket the time */
- X
- X for( cp = buf; *cp && !isspace( *cp ); cp++ )
- X continue;
- X if( *cp ) *cp++ = '\0';
- X else *cp = '\0';
- X
- X /*
- X * buf now at time field, figure offset until event,
- X * then fill in absolute time.
- X *
- X * gtime can't fail -- it will say it's 00:00 if it
- X * doesn't understand.
- X */
- X DPRINTF(stderr, "parseline: time buf %s\n", buf );
- X lp->timestr = strcpy( emalloc( strlen( buf ) + 1 ), buf );
- X (void) gtime( buf, &ttm );
- X t = (ttm.tm_hour * HRSECS) + (ttm.tm_min * 60);
- X lp->atime = d + t;
- X
- X /*
- X ** If past the event, and it's for any day, do it tomorrow.
- X ** BUG: This breaks if there is an interval after the event
- X ** This is a rare case, and I haven't yet thought of a clean fix.
- X */
- X if( anyday && lp->atime < Now )
- X lp->atime += HRSECS * 24;
- X
- X DPRINTF(stderr, "parseline: time offset %s is %d seconds, %02d:%02d\n",
- X buf, t, t / HRSECS, t % HRSECS );
- X DPRINTF(stderr, "parseline: etime %s\n", nctime(&lp->atime));
- X
- X /* advance to intervals */
- X
- X for( buf = cp; *buf && isspace(*buf); buf++)
- X continue;
- X
- X if (!*buf)
- X {
- X lp->type = BAD;
- X lp->errtype = NOINTERVALS;
- X lp->err = buf;
- X return FALSE;
- X }
- X
- X /* bracket the intervals */
- X
- X for( cp = buf; *cp && !isspace( *cp ); cp++ )
- X continue;
- X if( *cp ) *cp++ = '\0';
- X else *cp = '\0';
- X
- X /* save the interval string. */
- X
- X lp->intstr = strcpy( emalloc( strlen( buf ) + 1 ), buf );
- X
- X /* take rest of the line as the command */
- X
- X if (!*cp)
- X {
- X lp->type = BAD;
- X lp->errtype = NOCMD;
- X lp->err = strcpy( emalloc ( strlen( cp ) + 1 ), cp );
- X return FALSE;
- X }
- X
- X lp->cmd = strcpy( emalloc ( strlen( cp ) + 1 ), cp );
- X
- X return TRUE;
- X}
- X
- X
- X/*----------------
- X *
- X * zaplines() -- delete all NAGLINEs and free their space
- X *
- X */
- Xvoid
- Xzaplines()
- X{
- X register NAGLINE *lp;
- X register NAGLINE *nlp;
- X
- X for( lp = Flist; lp ; lp = nlp )
- X {
- X nlp = lp->next;
- X
- X if( lp->line )
- X free(lp->line);
- X if( lp->datestr )
- X free(lp->datestr);
- X if( lp->timestr )
- X free(lp->timestr);
- X if( lp->intstr )
- X free(lp->intstr);
- X if( lp->cmd )
- X free(lp->cmd);
- X
- X free( lp );
- X }
- X Flast = Flist = NULL;
- X}
- X
- X
- X/*----------------
- X *
- X * buildq() -- Rebuild the event queue if the .nag file has changed.
- X *
- X */
- Xvoid
- Xbuildq()
- X{
- X register NAGLINE *lp;
- X
- X DPRINTF(stderr, "buildq: rebuilding the event queue\n");
- X
- X zapq();
- X
- X for( lp = Flist; lp; lp = lp->next )
- X {
- X /* add events for silenced lines too. */
- X if( lp->type != COMMENT )
- X addevents( lp );
- X }
- X
- X sortq();
- X
- X if(Debug)
- X showevents( "Event queue after rebuild and sort\n" );
- X}
- X
- X
- X/*----------------
- X *
- X * zapq() -- Destroy an event queue, setting the head back to NULL.
- X *
- X * Only the actual element is freed.
- X */
- Xvoid
- Xzapq()
- X{
- X register EVENT *this;
- X register EVENT *next;
- X
- X for ( this = Evq; this ; this = next )
- X {
- X next = this->next;
- X free( this );
- X }
- X Evq = NULL;
- X}
- X
- X/*----------------
- X *
- X * insq() -- Add a new EVENT to the head of a queue.
- X *
- X */
- Xvoid
- Xinsq( etime, offset, lp )
- Xtime_t etime;
- Xregister int offset;
- XNAGLINE *lp;
- X{
- X register EVENT *ep;
- X
- X etime += (offset * 60);
- X
- X /* add events after last time we ran, but no more than 24 hours
- X in the future */
- X
- X if( ( etime >= Now || ( Last && etime > Last ) )
- X && etime < ( Now + ( HRSECS * 24 ) ) )
- X {
- X DPRINTF(stderr, "insq: Adding %s at %s\n", lp->cmd, nctime(&etime) );
- X }
- X else /* too late */
- X {
- X DPRINTF(stderr, "insq: Dropping %s at %s\n", lp->cmd, nctime(&etime) );
- X return;
- X }
- X
- X /*ALIGNOK*/
- X ep = (EVENT *) emalloc( sizeof(*ep) );
- X ep->etime = etime;
- X ep->offset = offset;
- X ep->lp = lp;
- X
- X /* splice into the head of the queue */
- X ep->next = Evq; /* NULL, if last event */
- X Evq = ep;
- X}
- X
- X
- X/*----------------
- X *
- X * addevents() -- Add pending events for the NAGLINE to the queue.
- X *
- X * Events in the past are not considered.
- X * If the command has been silenced, don't do the command.
- X *
- X */
- Xvoid
- Xaddevents( lp )
- Xregister NAGLINE *lp;
- X{
- X register char *cp; /* ptr into the interval string */
- X int offset; /* offset in minutes */
- X
- X /* for every numeric value in the interval string... */
- X
- X for( cp = lp->intstr; cp && *cp ; cp = index( cp, ':' ) )
- X {
- X if (*cp == ':') /* skip past optional ':' */
- X cp++;
- X if (!*cp) /* ignore trailing ':' */
- X return;
- X
- X /* read (possibly) signed interval value */
- X
- X if( 1 != sscanf( cp, "%d", &offset ) )
- X {
- X (void) fprintf(stderr, "%s: bad intervals '%s'\n", Myname,
- X lp->intstr );
- X return;
- X }
- X insq( lp->atime, offset, lp );
- X }
- X}
- X
- X
- X
- X/*----------------
- X *
- X * timecmp() -- Compare time of two events.
- X *
- X * Made slightly tricky since it must return an int, not a time_t.
- X *
- X */
- Xint
- Xtimecmp( a, b )
- Xregister EVENT **a;
- Xregister EVENT **b;
- X{
- X time_t val = (*a)->etime - (*b)->etime;
- X
- X return( val < 0 ? -1 : val > 0 );
- X}
- X
- X
- X/*----------------
- X *
- X * sortq() -- Sort the event queue into chronological order.
- X *
- X * 1. Create an array of pointers to the events in the queue.
- X * 2. Sort the array by time of the pointed-to events.
- X * 3. Rebuild the queue in the order of the array.
- X *
- X */
- Xvoid
- Xsortq()
- X{
- X register unsigned int n; /* number of events in the queue */
- X register unsigned int i; /* handy counter */
- X register EVENT **events; /* allocated array of EVENT ptrs */
- X register EVENT **ap; /* ptr into allocated events */
- X register EVENT *ep; /* pointer in event chain */
- X
- X forward int timecmp();
- X
- X n = 0;
- X for( ep = Evq; ep; ep = ep->next )
- X n++;
- X
- X DPRINTF(stderr, "sortq: %d events\n", n );
- X
- X if ( n < 2 )
- X return;
- X
- X /* build array of ptrs to events */
- X
- X /*ALIGNOK*/
- X ap = events = (EVENT **) ecalloc( (unsigned)sizeof(**ap), n );
- X
- X /* build array of ptrs to events */
- X for( ep = Evq; ep; ep = ep->next )
- X *ap++ = ep;
- X
- X /* sort by ascending time */
- X (void) qsort( events, (unsigned)n, sizeof(*events), timecmp );
- X
- X /* rechain the event queue from the sorted array */
- X Evq = ep = events[0];
- X for ( i = 0 ; i < n ; )
- X {
- X ep->next = events[i++];
- X ep = ep->next;
- X }
- X ep->next = NULL;
- X
- X free( events );
- X}
- X
- X
- X/*----------------
- X *
- X * runq() -- Execute all events that are due.
- X *
- X * Sleep until the next scheduled event. If there are none, or
- X * next is far away, sleep for MINSLEEP and try again.
- X *
- X */
- Xvoid
- Xrunq()
- X{
- X char cmd[ 5120 ];
- X char now[ CTIMELEN ];
- X register EVENT *evq; /* standin for global Evq in loop */
- X register EVENT *ep; /* next event */
- X register NAGLINE *lp;
- X int dsecs;
- X
- X DPRINTF(stderr, "runq start at %s\n", Nowstr );
- X
- X evq = Evq; /* fast access, be sure to save back */
- X
- X /*
- X * Execute commands that are due.
- X *
- X * Keeps head of the queue current by cutting out events as
- X * they are processed.
- X *
- X * The loop breaks out when the queue is gobbled up,
- X * or we get to an event that is not due now.
- X */
- X
- X while( evq && evq->etime <= Now )
- X {
- X lp = evq->lp;
- X
- X DPRINTF(stderr, "due at %s:\n", nctime( &evq->etime ) );
- X
- X /* Run a PENDING event */
- X
- X if( lp->type == PENDING && lp->cmd )
- X {
- X (void)strcpy( now, &Nowstr[ 11 ] );
- X now[ 5 ] = '\0';
- X
- X (void)sprintf( cmd, "pretime=%d;posttime=%d;now=%s;then=%s;%s\n",
- X -evq->offset,
- X evq->offset,
- X now,
- X nhour( &lp->atime ),
- X lp->cmd );
- X
- X DPRINTF(stderr, "executing:\n%s\n", cmd );
- X if( system( cmd ) )
- X (void) fprintf( stderr, "%s: Trouble running\n'%s'\n",
- X Myname, cmd );
- X }
- X
- X /* if it's a SILENT event, is it time to make it PENDING? */
- X
- X if( lp->type == SILENT )
- X {
- X /* find the queue end or the next event for the line */
- X
- X for( ep = evq->next ; ep && ep->lp != lp ; ep = ep->next )
- X continue;
- X
- X /* if match, or it was the last in the queue, turn it on */
- X
- X if ( ep )
- X {
- X DPRINTF(stderr, "SILENT event\n");
- X }
- X else
- X {
- X DPRINTF(stderr, "Last SILENT event, making PENDING again.\n");
- X lp->type = PENDING;
- X
- X /*
- X * if the write fails, keep going and hope the user fixes
- X * the nag file. If we exit, the daemon would need
- X * to be restarted by hand. Since it won't do anything
- X * but sleep and exit when the user logs off, no harm
- X * is done by sticking around.
- X */
- X (void) writef();
- X }
- X }
- X ep = evq->next;
- X free( evq );
- X evq = ep;
- X } /* for events on the queue */
- X
- X dsecs = evq ? min( evq->etime - Now, MINSLEEP) : MINSLEEP;
- X
- X DPRINTF(stderr, "sleeping for %d seconds, next %s\n",
- X dsecs,
- X evq ? nctime( &evq->etime ) : "never" );
- X
- X Evq = evq; /* back to global var */
- X
- X delay( dsecs );
- X}
- X
- X
- X/*----------------
- X *
- X * emalloc() -- malloc with error msg.
- X *
- X */
- Xchar *
- Xemalloc( size )
- Xregister int size;
- X{
- X register char *ptr;
- X extern char *malloc();
- X
- X if ( ( ptr = malloc( (unsigned) size ) ) == NULL )
- X {
- X (void) fprintf(stderr, "%s: Can't malloc %d bytes\n", Myname, size );
- X exit(1);
- X }
- X return( ptr );
- X}
- X
- X/*----------------
- X *
- X * ecalloc() -- calloc with error message.
- X *
- X */
- Xchar *
- Xecalloc( n, size )
- Xregister unsigned int n;
- Xregister unsigned int size;
- X{
- X register char *ptr;
- X extern char *calloc();
- X
- X if ( ( ptr = calloc( (unsigned) size, n ) ) == NULL )
- X {
- X (void) fprintf(stderr, "%s: Can't calloc %d bytes\n", Myname, size * n);
- X exit(1);
- X }
- X return( ptr );
- X}
- X
- X/*
- X * efopen() -- fopen with error message on failure (no fatal error)
- X */
- XFILE *
- Xefopen( file, mode )
- Xchar *file;
- Xchar *mode;
- X{
- X char buf [ 80 ];
- X register FILE * fp;
- X
- X if( (fp = fopen( file, mode )) == NULL )
- X {
- X (void)sprintf( buf, "%s: can't open file %s with mode \"%s\"",
- X Myname, file, mode );
- X perror( buf );
- X }
- X return( fp );
- X}
- X
- X
- X/*
- X * showline() -- Dump the line list.
- X */
- Xvoid
- Xshowlines( msg )
- Xchar *msg;
- X{
- X register NAGLINE *lp;
- X
- X (void) fprintf(stderr, "%s", msg );
- X for( lp = Flist; lp ; lp = lp->next )
- X dumpline( lp );
- X}
- X
- X/*
- X * dumpline() -- dump a NAGLINE for debugging.
- X */
- Xvoid
- Xdumpline( lp )
- Xregister NAGLINE *lp;
- X{
- X if( lp == NULL )
- X {
- X (void) fprintf(stderr, "dumpline: NULL lp\n");
- X return;
- X }
- X (void) fprintf(stderr, "\nline (%s):\n%s\n", linetypes[ lp->type ],
- X lp->line );
- X switch( lp->type )
- X {
- X case BAD:
- X (void) fprintf(stderr, "%s %s\n", parserrs[ lp->errtype ], lp->err );
- X break;
- X
- X case PENDING:
- X case SILENT:
- X (void) fprintf(stderr, "The event is at %s\n", nctime( &lp->atime ));
- X }
- X}
- X
- X/*
- X * showevents() -- dump the event list, for debugging.
- X */
- Xvoid
- Xshowevents( msg )
- Xchar *msg;
- X{
- X register EVENT *ep;
- X
- X (void) fprintf(stderr, "%s", msg );
- X for( ep = Evq; ep; ep = ep->next )
- X dumpevent( ep );
- X}
- X
- X/*
- X * dumpevent() -- print an event, for debugging.
- X */
- Xvoid
- Xdumpevent( ep )
- Xregister EVENT *ep;
- X{
- X if( ep == NULL )
- X (void) fprintf(stderr, "dumpevent: NULL ep\n");
- X else
- X (void) fprintf(stderr, "event 0x%x, next 0x%x offset %d time %s\n",
- X ep, ep->next, ep->offset, nctime(&ep->etime) );
- X}
- X
- X/*
- X * nctime() -- ctime with trailing '\n' whacked off.
- X */
- Xchar *
- Xnctime( t )
- Xtime_t *t;
- X{
- X register char *cp;
- X
- X cp = ctime( t );
- X cp[ strlen( cp ) - 1 ] = '\0';
- X return ( cp );
- X}
- X
- X/*
- X * nhour() -- return an hh:mm string given a pointer to a time_t.
- X */
- Xchar *
- Xnhour( t )
- Xtime_t *t;
- X{
- X register char *buf = ctime( t );
- X
- X /*
- X * 012345678901234567890123
- X * Wed Dec 31 16:00:00 1969
- X */
- X
- X buf[ 16 ] = '\0';
- X return ( &buf[ 11 ] );
- X}
- X
- X
- X/*----------------
- X *
- X * delay() -- like sleep but knows what 0 means.
- X *
- X * If user logs out, notices and exit with OK status.
- X *
- X */
- Xvoid
- Xdelay( secs )
- Xint secs;
- X{
- X char thislogin[20];
- X
- X if( secs > 0)
- X {
- X (void) sleep( (unsigned) secs );
- X (void) strcpy(thislogin, getlogin());
- X if ( strcmp(Origlogin, thislogin) )
- X exit(0);
- X }
- X}
- X
- X/*
- X * lowcase() -- make a string all lower case.
- X */
- Xvoid
- Xlowcase( s )
- Xchar *s;
- X{
- X while ( *s )
- X {
- X if( isupper( *s ) )
- X *s = tolower( *s );
- X s++;
- X }
- X}
- X
- X# if 0
- X
- X/*
- X * dumptm() -- show contents of a tm structure.
- X */
- Xdumptm( tm )
- Xstruct tm *tm;
- X{
- X (void) fprintf(stderr, "year : %d month: %d day: %d\n",
- X tm->tm_year,tm->tm_mon,tm->tm_mday);
- X (void) fprintf(stderr, "day of month: %d hour: %d minute: %d second: %d\n",
- X tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec) ;
- X (void) fprintf(stderr, "day of year: %d day of week: %d dst: %d\n",
- X tm->tm_yday, tm->tm_wday, tm->tm_isdst) ;
- X}
- X
- X# endif
- X
- X/* end of nag.c */
- X
- X
- SHAR_EOF
- if test 29846 -ne "`wc -c 'nag.c'`"
- then
- echo shar: error transmitting "'nag.c'" '(should have been 29846 characters)'
- fi
- # End of shell archive
- exit 0
-
-